﻿/*
 * Created by SharpDevelop.
 * User: BinderNews
 * Date: 9/12/2011
 * Time: 11:12 PM
 * 
 * This file hold all the helper routines used by the Assembler class.
 * The purpose is to keep the 
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Globalization;
using System.Collections;

namespace tiDE
{
	/// <summary>
	/// Represents the main core of the Assembler
	/// </summary>
	public partial class Assembler
	{
		#region Helpers

        public static byte[] BytesFromBinary(string binary)
        {
            while (binary.Length % 8 != 0)
                binary = "0" + binary; // Make sure the value lines up as a byte
            byte[] result = new byte[binary.Length / 8];
            byte currentValue = 0;
            byte mask = 0x80;
            int index = 0;
            foreach (char c in binary)
            {
                if (c == '1')
                {
                    currentValue += mask;
                }
                mask = (byte)(mask >> 1); // Rotate the mask
                if (mask == 0)
                {
                    mask = 0x80;
                    result[index] = currentValue;
                    currentValue = 0;
                    index++;
                }
            }
            return result;
        }

        private ushort UshortFromBinary(string binary)
        {
            while (binary.Length % 8 != 0)
                binary = "0" + binary; // Make sure the value lines up as a byte
            ushort result = 0;
            ushort mask = 0x8000;
            foreach (char c in binary)
            {
                if (c == '1')
                {
                    result += mask;
                }
                mask = (ushort)(mask >> 1); // Rotate the mask
                if (mask == 0)
                    mask = 0x8000;
            }
            return result;
        }

        /// <summary>
        /// This is called when text is output, as an alternitave
        /// to System.Console.WriteLine
        /// </summary>
        public event EventHandler<OutputTextEventArgs> TextOutputRequested;

        /// <summary>
        /// Output text to the command line and the calling library
        /// </summary>
        private void OutputText(string Text)
        {
            Console.WriteLine(Text);
            if (TextOutputRequested != null)
                TextOutputRequested(this, new OutputTextEventArgs(Text));
        }

        /// <summary>
        /// Adds an error
        /// </summary>
        private void AddError(AssemblerError Error)
        {
            if (noList || ignoreErrors)
                return;
            Output.Errors.Add(Error);
            OutputText(Error.Type.ToString() + " Error: Line " + Error.Line.ToString() + ":" +
                Error.IndexOf.ToString() + ": " + Error.UserMessage);
        }

        /// <summary>
        /// Parses an expression, performing all required math, and
        /// returns the binary equivalent with the specified number of bits.
        /// This handles references and constants as well.  Use this
        /// method to parse code like this:
        /// ld hl, PlotsScreen - (12 * 16h) + 2 + 'd'
        /// This method adds an error and returns an empty value if neccissary
        /// </summary>
        private string ParseExpression(string Expression, int numBits,
            bool offsetFromCurrent = false, int subtractFromValue = 0, bool negateValue = false, bool LittleEndian = true, int referenceOffset = 0)
        {
            string value = "";
            // How this is done:
            // First, find all constants and replace them with their decimal equivalents
            // Replace all hex/binary/characters with their decimal equivalents
            // Create a stack of operations, push them to the stack according to order of operations
            // Pop each item from the stack and execute it
            // Check that the expression fits the number of available bits
            // If it doesn't, add an error and return the default value
            // Otherwise, return the result

            ushort parsedValue = 0;

            if (UseOrderOfOperations)
            {
                parsedValue = ParseSubexpressionOOO(Expression, offsetFromCurrent, referenceOffset);
            }
            else
            {
            	//TODO: BinderNews: Add left-to-right parsing code
            }

            //if (offsetFromCurrent)
            //    parsedValue = (ushort)(parsedValue - (ushort)OutputBytes.Count);
            parsedValue -= (ushort)subtractFromValue;
            if (negateValue)
                parsedValue = (ushort)-parsedValue;
            value = Convert.ToString(parsedValue, 2);

            if (numBits != -1)
            {
                if (value.Length > numBits)
                    value = value.Substring(numBits);
                while (value.Length < numBits)
                    value = "0" + value;
                while (value.Length > numBits)
                    value = value.Remove(value.Length - 1);
                if (numBits == 16 && LittleEndian)
                    value = value.Substring(8) + value.Remove(8); // Little endian
            }
            return value;
        }

        private int FindClosingParenthesis(string Value, int Opening)
        {
            int numParenthesis = 0, length = 0;
            for (int i = Opening; i < Value.Length; i++)
            {
                if (Value[i] == '(')
                    numParenthesis++;
                if (Value[i] == ')')
                    numParenthesis--;
                if (numParenthesis == 0)
                    return length;
                length++;
            }
            return -1;
        }

        /// <summary>
        /// Parses a subexpression using order of operations
        /// </summary>
        /// <param name="Expression"></param>
        /// <returns></returns>
        private ushort ParseSubexpressionOOO(string Expression, bool offestFromCurrent = false, int referenceOffset = 0)
        {
            // Recursively parse parenthesis first
            if (Expression.Contains("("))
            {
                int opening = Expression.IndexOf("(");
                ushort value = ParseSubexpressionOOO(Expression.Substring(opening + 1,
                    FindClosingParenthesis(Expression, opening) - 1));
                Expression = Expression.Remove(opening,
                    FindClosingParenthesis(Expression, opening) + 1);
                Expression = Expression.Insert(opening, value.ToString());
            }
            bool negative = false;
            if (Expression.StartsWith("-"))
            {
                negative = true;
                Expression = Expression.Substring(1);
            }
            while (HasMathSymbols(Expression))
            {
                if (Expression.Contains("*"))
                {
                    string value1 = Expression.Substring(0, Expression.IndexOf("*"));
                    string value2 = Expression.Substring(Expression.IndexOf("*") + 1);
                    int startIndex = 0, length = 0;
                    for (int i = value1.Length - 1; i != -1; i--)
                    {
                    	if ("*-+/|^&".Contains(value1[i].ToString()))
                        {
                            value1 = value1.Substring(i + 1);
                            startIndex = i + 1;
                            break;
                        }
                    }
                    for (int i = 0; i < value2.Length; i++)
                    {
                    	if ("*-+/|^&".Contains(value2[i].ToString()))
                        {
                            value2 = value2.Remove(i);
                            length = value1.Length + i + 1;
                            break;
                        }
                    }
                    if (length == 0)
                        length = Expression.Length - startIndex;
                    ushort result = (ushort)(ParseValue(value1, offestFromCurrent, referenceOffset) * ParseValue(value2, offestFromCurrent, referenceOffset));
                    Expression = Expression.Remove(startIndex, length);
                    Expression = Expression.Insert(startIndex, result.ToString());
                    continue;
                }
                if (Expression.Contains("/"))
                {
                    string value1 = Expression.Substring(0, Expression.IndexOf("/"));
                    string value2 = Expression.Substring(Expression.IndexOf("/") + 1);
                    int startIndex = 0, length = 0;
                    for (int i = value1.Length - 1; i != -1; i--)
                    {
                    	if ("*-+/|^&".Contains(value1[i].ToString()))
                        {
                            value1 = value1.Substring(i + 1);
                            startIndex = i + 1;
                            break;
                        }
                    }
                    for (int i = 0; i < value2.Length; i++)
                    {
                    	if ("*-+/|^&".Contains(value2[i].ToString()))
                        {
                            value2 = value2.Remove(i);
                            length = value1.Length + i + 1;
                            break;
                        }
                    }
                    if (length == 0)
                        length = Expression.Length;
                    ushort result = (ushort)(ParseValue(value1, offestFromCurrent, referenceOffset) / ParseValue(value2, offestFromCurrent, referenceOffset));
                    Expression = Expression.Remove(startIndex, length);
                    Expression = Expression.Insert(startIndex, result.ToString());
                    continue;
                }
                if (Expression.Contains("+"))
                {
                    string value1 = Expression.Substring(0, Expression.IndexOf("+"));
                    string value2 = Expression.Substring(Expression.IndexOf("+") + 1);
                    int startIndex = 0, length = 0;
                    for (int i = value1.Length - 1; i != -1; i--)
                    {
                    	if ("*-+/|^&".Contains(value1[i].ToString()))
                        {
                            value1 = value1.Substring(i + 1);
                            startIndex = i + 1;
                            break;
                        }
                    }
                    for (int i = 0; i < value2.Length; i++)
                    {
                    	if ("*-+/|^&".Contains(value2[i].ToString()))
                        {
                            value2 = value2.Remove(i);
                            length = value1.Length + i + 1;
                            break;
                        }
                    }
                    if (length == 0)
                        length = Expression.Length;
                    ushort result = (ushort)(ParseValue(value1, offestFromCurrent, referenceOffset) + ParseValue(value2, offestFromCurrent, referenceOffset));
                    Expression = Expression.Remove(startIndex, length);
                    Expression = Expression.Insert(startIndex, result.ToString());
                    continue;
                }
                if (Expression.Contains("-"))
                {
                    string value1 = Expression.Substring(0, Expression.IndexOf("-"));
                    string value2 = Expression.Substring(Expression.IndexOf("-") + 1);
                    int startIndex = 0, length = 0;
                    for (int i = value1.Length - 1; i != -1; i--)
                    {
                    	if ("*-+/|^&".Contains(value1[i].ToString()))
                        {
                            value1 = value1.Substring(i + 1);
                            startIndex = i + 1;
                            break;
                        }
                    }
                    for (int i = 0; i < value2.Length; i++)
                    {
                    	if ("*-+/|^&".Contains(value2[i].ToString()))
                        {
                            value2 = value2.Remove(i);
                            length = value1.Length + i + 1;
                            break;
                        }
                    }
                    if (length == 0)
                        length = Expression.Length;
                    ushort result = (ushort)(ParseValue(value1, offestFromCurrent, referenceOffset) - ParseValue(value2, offestFromCurrent, referenceOffset));
                    Expression = Expression.Remove(startIndex, length);
                    Expression = Expression.Insert(startIndex, result.ToString());
                    continue;
                }
            }
            if (negative)
                return (ushort)(-(int)ParseValue(Expression, offestFromCurrent, referenceOffset));
            return ParseValue(Expression, offestFromCurrent, referenceOffset);
        }

        private bool HasMathSymbols(string Expression)
        {
            bool inString = false;
            bool inChar = false;
            foreach (char c in Expression)
            {
                if (c == '"' && !inString)
                    inChar = !inChar;
                if (c == '\'' && !inChar)
                    inString = !inString;
                if ("+-*/|&^".Contains(c.ToString()) && !inString && !inChar)
                    return true;
            }
            return false;
        }

        private string GetWithinParenthesis(string Value)
        {
            int startIndex = -1, length = 0;
            int numOpen = 0, i = 0;
            foreach (char c in Value)
            {
                if (c == '(')
                {
                    if (numOpen == 0)
                        startIndex = i;
                    numOpen++;
                }
                if (c == ')')
                {
                    numOpen--;
                    if (numOpen == 0)
                        break;
                }
                if (numOpen != 0)
                    length++;
                i++;
            }
            if (startIndex == -1)
                return "";
            return Value.Substring(startIndex, length);
        }

        //TODO: provide explanation for params
        /// <summary>
        /// Parses a literal value, such as "%11011011"
        /// or "PlotsScreen"
        /// </summary>
        /// <param name="literal"></param>
        /// <param name="offsetFromCurrent"></param>
        /// <returns></returns>
        private ushort ParseValue(string literal, bool offsetFromCurrent = false, int referenceOffset = 0)
        {
            if (passTwo)
            {
                //System.Diagnostics.Debugger.Break();
            }
            ushort result = 0;
            literal = literal.ToLower().Trim();
            if (string.IsNullOrEmpty(literal))
            {
                AddError(new AssemblerError()
                {
                    UserMessage = "Invalid Number",
                    Type = AssemblerErrorType.Syntax,
                });
                return 0;
            }
            if (literal == "$")
            {
                result = (ushort)(OutputBytes.Count + CurrentOrigin);
            }
            else if (!"0123456789$%".Contains(literal[0].ToString()))
            {
                // Look for constant/label
                if (Constants.ContainsKey(literal.ToLower()))
                    return UshortFromBinary(Constants[literal].Value);
                foreach (SavedInclude inc in SavedIncludeFiles.Values)
                {
                    if (inc.Constants.ContainsKey(literal.ToLower()))
                        return UshortFromBinary(inc.Constants[literal].Value);
                }

                if (Labels.ContainsKey(literal))
                {
                    if (!offsetFromCurrent && passTwo)
                    {
                        References.Add(new Reference(literal, (ushort)(OutputBytes.Count + referenceOffset))); // TODO: Make not a dictionary
                    }
                    if (!offsetFromCurrent)
                        return 0;
                    if (offsetFromCurrent)
                        return (ushort)(Labels[literal] - (OutputBytes.Count + CurrentOrigin));
                    return Labels[literal];
                }
                else
                {
                    if (!noList && !addNoRefs && passTwo)
                    {
                        References.Add(new Reference(literal, (ushort)(OutputBytes.Count + referenceOffset)));
                    }
                    return 0;
                }
            }
            else if (literal[0] == '$')
            {
                // Hex
                literal = literal.Substring(1);
                bool isValid = true;
                foreach (char c in literal)
                {
                	if (!"abcdef0123456789".Contains(c.ToString()))
                    {
                        AddError(new AssemblerError()
                        {
                            UserMessage = "Invalid Number",
                            Type = AssemblerErrorType.Syntax,
                        });
                        isValid = false;
                    }
                }
                if (isValid)
                    result = Convert.ToUInt16(literal, 16); // Valid hex number
            }
            else if (literal[0] == '%')
            {
                // Binary
                literal = literal.Substring(1);
                bool isValid = true;
                foreach (char c in literal)
                {
                    if (c != '0' && c != '1')
                    {
                        AddError(new AssemblerError()
                        {
                            UserMessage = "Invalid Number",
                            Type = AssemblerErrorType.Syntax,
                        });
                        isValid = false;
                    }
                }
                if (isValid)
                    result = Convert.ToUInt16(literal, 2); // Valid binary number
            }
            else if (char.IsNumber(literal[0]))
            {
                // Number of some sort
                if (literal.EndsWith("h"))
                {
                    // Hex
                    literal = literal.Remove(literal.Length - 1);
                    bool isValid = true;
                    foreach (char c in literal)
                    {
                    	if (!"abcdef0123456789".Contains(c.ToString()))
                        {
                            AddError(new AssemblerError()
                            {
                                UserMessage = "Invalid Number",
                                Type = AssemblerErrorType.Syntax,
                            });
                            isValid = false;
                        }
                    }
                    if (isValid)
                        result = Convert.ToUInt16(literal, 16); // Valid hex number
                }
                else if (literal.EndsWith("b"))
                {
                    // Binary
                    literal = literal.Remove(literal.Length - 1);
                    bool isValid = true;
                    foreach (char c in literal)
                    {
                        if (c != '0' && c != '1')
                        {
                            AddError(new AssemblerError()
                            {
                                UserMessage = "Invalid Number",
                                Type = AssemblerErrorType.Syntax,
                            });
                            isValid = false;
                        }
                    }
                    if (isValid)
                        result = Convert.ToUInt16(literal, 2); // Valid binary number
                }
                else if (literal[0] == '\'')
                {
                    // Character
                    result = (ushort)Convert.ToByte(literal[1]);
                }
                else
                {
                    // Decimal
                    bool isValid = true;
                    foreach (char c in literal)
                    {
                        if (!char.IsNumber(c))
                        {
                            AddError(new AssemblerError()
                            {
                                UserMessage = "Invalid Number",
                                Type = AssemblerErrorType.Syntax,
                            });
                            isValid = false;
                        }
                    }
                    if (isValid)
                        result = Convert.ToUInt16(literal, 10); // Valid decimal number
                }
            }
            if (!offsetFromCurrent)
                return result;
            else
                return (ushort)((OutputBytes.Count + CurrentOrigin) - result);
        }

        /// <summary>
        /// A regular expression that will match a label
        /// </summary>
        public static readonly Regex LabelRegex = new Regex(@"^\s*[A-Za-z0-9_]+\s*:.*",RegexOptions.Compiled);
        
        /// <summary>
        /// Returns true if the label's name is valid
        /// Label names may not contain whitespace or any non-alphanumeric
        /// character other than the underscore.
        /// </summary>
        private bool IsLabelValid(string Label)
        {	
        	return LabelRegex.IsMatch(Label);
        	/*
        	Label = Label.Trim();
            Label = Label.Remove(Label.LastIndexOf(":")).Trim(); // Trim and remove the identifying colon

            foreach (char c in Label)
                if (!"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".Contains(c)) // Valid label characters
                    return false;

            return true;
            */
        }

        bool passTwo = false;

        /// <summary>
        /// Compares Value with Opcode to see if they
        /// match according to the opcode definition
        /// </summary>
        public OpcodeComparisonResult OpcodeCompare(string Opcode, string Value)
        {
            List<int> hardwareIndicies = new List<int>();
            List<int> immediateIndicies = new List<int>();
            List<int> hardwareLengths = new List<int>();
            List<int> immediateLengths = new List<int>();
            List<string> hardwareTables = new List<string>();
            List<string> immIndic = new List<string>();
            int opcodeIndex = 0, numImmParenthesis = 0;
            bool newImm = true, requiredWhitespaceMet = false, newHWOp = true, immValueUsesParenthesis = false;
            Value = Value.Trim().ToLower();
            try
            {
                for (int i = 0; i < Value.Length; i++)
                {
                    // Handle immediate values
                    if (Opcode[opcodeIndex] == '%' || Opcode[opcodeIndex] == '&' || // 16-bit unsigned, 8-bit unsigned
                        Opcode[opcodeIndex] == '*' || Opcode[opcodeIndex] == '^' || // 8-bit signed, and 8-bit negitave signed
                        Opcode[opcodeIndex] == '!' || Opcode[opcodeIndex] == '#')   // 2-bit positive, and 2-bit negative
                    {
                        if (newImm)
                        {
                            newImm = false;
                            immValueUsesParenthesis = false;
                            immediateIndicies.Add(i);
                            immIndic.Add(Opcode[opcodeIndex].ToString() + Opcode[opcodeIndex + 1]);
                            if (Opcode[opcodeIndex - 2] == '(')
                                numImmParenthesis = 1;
                        }
                        if (Value[i] == '(')
                        {
                            numImmParenthesis++;
                            immValueUsesParenthesis = true;
                        }
                        if (Value[i] == ')')
                        {
                            numImmParenthesis--;
                            immValueUsesParenthesis = true;
                        }
                        bool isTerminated = Value[i] == ',' || (Opcode.Contains('(') && i == Value.Length - 1);
                        if (immValueUsesParenthesis && Opcode.Contains('('))
                            isTerminated = numImmParenthesis <= 0;
                        if (isTerminated) //Immediate values are terminated by commas/parenthesis
                        {
                            immediateLengths.Add(i - immediateIndicies.Last());
                            i--;
                            opcodeIndex += 2;
                            newImm = true;
                            if (opcodeIndex >= Opcode.Length)
                                break;
                        }
                    }
                    else if (Opcode[opcodeIndex] == '$') // Handle hardware directives
                    {
                        if (newHWOp)
                        {
                            newHWOp = false;
                            hardwareTables.Add(Opcode[opcodeIndex + 1].ToString() + Opcode[opcodeIndex + 2]);
                            hardwareIndicies.Add(i);
                        }
                        // Hardware operands are terminated by nonalphabetical characters (AF' is a special exception)
                        bool terminated = !"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz()".Contains(Value[i]);
                        if (hardwareTables.Last().StartsWith("I"))
                            terminated = !"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz$%".Contains(Value[i]);
                        if (terminated)
                        {
                            hardwareLengths.Add(i - hardwareIndicies.Last());
                            i--;
                            opcodeIndex += 3;
                            newHWOp = true;
                            bool foundOperand = false;
                            string value = Value.Substring(hardwareIndicies.Last(),
                                hardwareLengths.Last()).ToLower().Replace(" ", "").Replace("\t", "");
                            if (hardwareTables.Last()[0].ToString() == "I")
                            {
                                value = ((byte)ParseValue(value)).ToString();
                            }
                            foreach (HardwareOperand op in OperandTable["$" + hardwareTables.Last()[0].ToString()])
                            {
                                if (op.Operand.ToLower() == value)
                                {
                                    foundOperand = true;
                                    break;
                                }
                            }
                            if (!foundOperand)
                            {
                                return new OpcodeComparisonResult()
                                {
                                    IsMatch = false,
                                    InvalidOperand = true,
                                };
                            }
                        }
                    }
                    else if (Opcode[opcodeIndex] == '_') // Required whitespace
                    {
                        if (!requiredWhitespaceMet)
                        {
                            if (string.IsNullOrWhiteSpace(Value[i].ToString()))
                            {
                                requiredWhitespaceMet = true;
                            }
                            else
                            {
                                return new OpcodeComparisonResult()
                                {
                                    IsMatch = false,
                                };
                            }
                        }
                        if (!string.IsNullOrWhiteSpace(Value[i].ToString()))
                        {
                            opcodeIndex++;
                            i--;
                            requiredWhitespaceMet = false;
                        }
                    }
                    else if (Opcode[opcodeIndex] == '.') // Optional whitespace
                    {
                        if (!string.IsNullOrWhiteSpace(Value[i].ToString()))
                        {
                            opcodeIndex++;
                            i--;
                        }
                    }
                    else // Compare required characters
                    {
                        if (Opcode[opcodeIndex] != Value[i])
                        {
                            return new OpcodeComparisonResult()
                            {
                                IsMatch = false,
                            };
                        }
                        opcodeIndex++;
                    }
                }
            }
            catch
            {
                return new OpcodeComparisonResult()
                {
                    IsMatch = false,
                };
            }
            if (!newImm) // Handle end of string immediate values/hardware operands
            {
                immediateLengths.Add(Value.Length - immediateIndicies.Last());
                opcodeIndex += 2;
            }
            if (!newHWOp)
            {
                hardwareLengths.Add(Value.Length - hardwareIndicies.Last());
                opcodeIndex += 3;
                bool foundOperand = false;
                string value = Value.Substring(hardwareIndicies.Last(),
                        hardwareLengths.Last()).ToLower().Replace(" ", "").Replace("\t", "");
                if (hardwareTables.Last()[0].ToString() == "I")
                {
                    value = ((byte)ParseValue(value)).ToString("x");
                }
                foreach (HardwareOperand op in OperandTable["$" + hardwareTables.Last()[0].ToString()])
                {
                    if (op.Operand.ToLower() == value)
                    {
                        foundOperand = true;
                        break;
                    }
                }
                if (!foundOperand)
                {
                    return new OpcodeComparisonResult()
                    {
                        IsMatch = false,
                        InvalidOperand = true,
                    };
                }
            }
            if (opcodeIndex != Opcode.Length)
            {
                return new OpcodeComparisonResult()
                {
                    IsMatch = false,
                };
            }
            return new OpcodeComparisonResult()
            {
                HardwareOperandIndicies = hardwareIndicies.ToArray(),
                HardwareOperandLengths = hardwareLengths.ToArray(),
                ImmediateValueIndicies = immediateIndicies.ToArray(),
                ImmediateValueLengths = immediateLengths.ToArray(),
                HardwareTablesUsed = hardwareTables.ToArray(),
                ImmediateValueIdentifier = immIndic.ToArray(),
                IsMatch = true,
            };
        }

        public struct OpcodeComparisonResult
        {
            /// <summary>
            /// The location within the provided value where hardware
            /// operands are found
            /// </summary>
            public int[] HardwareOperandIndicies;
            /// <summary>
            /// The lengths of each hardware operand as specified by
            /// OpcodeComparisonResult.HardwareOperandIndicies
            /// </summary>
            public int[] HardwareOperandLengths;
            /// <summary>
            /// The location within the provided value where immediate
            /// values are found
            /// </summary>
            public int[] ImmediateValueIndicies;
            /// <summary>
            /// The lengths of eachimmediate value as specified by
            /// OpcodeComparisonResult.ImmediateValueIndicies
            /// </summary>
            public int[] ImmediateValueLengths;
            /// <summary>
            /// The hardware operand tables used in this opcode
            /// </summary>
            public string[] HardwareTablesUsed;
            /// <summary>
            /// True if the value matches the specified opcode
            /// </summary>
            public bool IsMatch;
            /// <summary>
            /// The character identifying the kind of immediate value in use
            /// </summary>
            public string[] ImmediateValueIdentifier;
            /// <summary>
            /// This is true if it failed because of a bad operand
            /// </summary>
            public bool InvalidOperand;
        }

        bool MultilineComment = false;

        /// <summary>
        /// Removes the comments from a line of source code
        /// </summary>
        /// <param name="Code"></param>
        /// <returns></returns>
        private string InternalStripComments(string Code)
        {
            Code = Code.Trim();
            bool isInString = false, isInChar = false;
            if (MultilineComment)
            {
                if (Code.Contains("*/"))
                {
                    MultilineComment = false;
                    return Code.Substring(Code.IndexOf("*/") + 2);
                }
                return "";
            }
            if (Code.StartsWith(";") || Code.StartsWith("//"))
                return "";
            for (int i = 0; i < Code.Length; i++)
            {
                if (Code[i] == ';' && !isInString && !isInChar)
                {
                    Code = Code.Remove(i);
                    return Code;
                }
                if (i + 1 < Code.Length && Code[i] == '/' && Code[i + 1] == '/')
                {
                    Code = Code.Remove(i);
                    return Code;
                }
                if (i + 1 < Code.Length && Code[i] == '/' && Code[i + 1] == '*')
                {
                    int end = Code.Length - i;
                    if (Code.Contains("*/"))
                    {
                        end = Code.IndexOf("*/") - i + 2;
                    }
                    else
                        MultilineComment = true;
                    Code = Code.Remove(i, end);
                    return Code;
                }
                if (Code[i] == '"' && !isInChar)
                {
                    isInString = !isInString;
                }
                if (Code[i] == '\'' && !isInString)
                {
                    isInChar = !isInChar;
                }
            }
            return Code;
        }

        public static string StripComments(string Code)
        {
            Code = Code.Trim();
            bool isInString = false, isInChar = false;
            //I'm presuming this is meant to temporarily
            //override the instance variable
            bool MultilineComment = false;
            if (MultilineComment)
            {
                if (Code.Contains("*/"))
                {
                    MultilineComment = false;
                    return Code.Substring(Code.IndexOf("*/") + 2);
                }
                return "";
            }
            if (Code.StartsWith(";") || Code.StartsWith("//"))
                return "";
            for (int i = 0; i < Code.Length; i++)
            {
                if (Code[i] == ';' && !isInString && !isInChar)
                {
                    Code = Code.Remove(i);
                    return Code;
                }
                if (i + 1 < Code.Length && Code[i] == '/' && Code[i + 1] == '/')
                {
                    Code = Code.Remove(i);
                    return Code;
                }
                if (i + 1 < Code.Length && Code[i] == '/' && Code[i + 1] == '*')
                {
                    int end = Code.Length - i;
                    if (Code.Contains("*/"))
                    {
                        end = Code.IndexOf("*/") - i + 2;
                    }
                    else
                        MultilineComment = true;
                    Code = Code.Remove(i, end);
                    return Code;
                }
                if (Code[i] == '"' && !isInChar)
                {
                    isInString = !isInString;
                }
                if (Code[i] == '\'' && !isInString)
                {
                    isInChar = !isInChar;
                }
            }
            return Code;
        }

        #endregion
	}
}
